package com.lch.base.http.config;

import android.content.Context;
import android.support.annotation.RawRes;

import com.lch.base.http.cache.CookieCacheImpl;
import com.lch.base.http.cookie.NovateCookieManager;
import com.lch.base.http.cookie.SharedPrefsCookiePersistor;
import com.lch.base.http.interceptor.CacheInterceptor;
import com.lch.base.http.utils.HttpsSslFactroy;

import java.io.InputStream;
import java.net.Proxy;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;

import okhttp3.Cache;
import okhttp3.CertificatePinner;
import okhttp3.ConnectionPool;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient.Builder;
import okhttp3.logging.HttpLoggingInterceptor;

/**
 * okhttp设置提供者
 *
 * @author lee(29487113@qq.com) on 18/2/4
 */
public class OkHttpProvide {
    private static final int DEFAULT_TIMEOUT = 10; //默认超时时间

    private static final int DEFAULT_MAXIDLE_CONNECTIONS = 5;
    private static final long DEFAULT_KEEP_ALIVEDURATION = 8;

    public static Builder provideOkHttpBuild(Context context, boolean isShowLog, boolean isCookie, CacheProvide.CACHE_TYPE cacheType) {
        Builder builder = new Builder()
//                .connectionPool(new ConnectionPool(DEFAULT_MAXIDLE_CONNECTIONS, DEFAULT_KEEP_ALIVEDURATION, TimeUnit.SECONDS)) //设置线程池 // TODO: 2017/12/22
//                .retryOnConnectionFailure(true)
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);

        //Level.BODY的介绍 http://blog.csdn.net/love_xsq/article/details/55257936
        if (isShowLog) {
            builder.addNetworkInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
        }

        switch (cacheType) {
            //如果服务器支持缓存，请求返回的Response会带有这样的Header:Cache-Control, max-age=xxx,这种情况下我们只需要手动给okhttp设置缓存就可以让okhttp自动帮你缓存了。这里的max-age的值代表了缓存在你本地存放的时间。
            case CACHE_SERVER:
                Cache cache = CacheProvide.provideCache(context);
                builder.cache(cache);
                break;


            /**
             * https://www.jianshu.com/p/5cd7e95c2f29
             * http://blog.csdn.net/qq_33463102/article/details/60869879必须看缓存设置
             * https://www.jianshu.com/p/e3d32c016c32
             * http://www.sohu.com/a/124644067_608959 你不知道的Retrofit缓存库RxCache 替换
             * http://blog.csdn.net/oyangyujun/article/details/50039403 拦截器的区分 待写文章
             * 有1.addInterceptor ,和2.addNetworkInterceptor这两种。他们的区别简单的说下，不知道也没关系，addNetworkInterceptor添加的是网络拦截器，他会在在request和resposne是分别被调用一次，addinterceptor添加的是aplication拦截器，他只会在response被调用一次
             */
            case CACHE_CLIENT:
                Cache cacheClient = CacheProvide.provideCache(context);
                CacheInterceptor cacheInterceptor = new CacheInterceptor(context);
                builder.addInterceptor(cacheInterceptor); //无网络的时候也走。。貌似不需要设置也行。。
                builder.addNetworkInterceptor(cacheInterceptor);
                builder.cache(cacheClient);
                break;

            default:
                break;
        }

        if (isCookie) {
            NovateCookieManager cookie = new NovateCookieManager(new CookieCacheImpl(), new SharedPrefsCookiePersistor(context));
            cookieJar(builder, cookie);
        }

        addUnSafeSslSocketAndHostnameVerifier(builder);


        return builder;
    }


    public static Builder setTimeOut(Builder builder, long connectTimeout, long readTimeout, long writeTimeout) {
        if (connectTimeout > 0) {
            builder.connectTimeout(connectTimeout, TimeUnit.SECONDS);
        }

        if (readTimeout > 0) {
            builder.readTimeout(connectTimeout, TimeUnit.SECONDS);
        }

        if (writeTimeout > 0) {
            builder.writeTimeout(connectTimeout, TimeUnit.SECONDS);
        }
        return builder;
    }

    /**
     * 设置超时时间，统一一个时间
     *
     * @param timeout
     * @return
     */
    public static Builder setTimeOut(Builder builder, long timeout) {
        if (timeout > 0) {
            builder.connectTimeout(timeout, TimeUnit.SECONDS);
            builder.readTimeout(timeout, TimeUnit.SECONDS);
            builder.writeTimeout(timeout, TimeUnit.SECONDS);
        }
        return builder;
    }


    /**
     * 设置线程池的大小
     *
     * @return
     */
    public static Builder connectionPool(Builder builder, int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
        if (maxIdleConnections < 0 || keepAliveDuration < 0) {
            throw new NullPointerException("connectionPool value must > 0");
        }
        builder.connectionPool(new ConnectionPool(maxIdleConnections, keepAliveDuration, timeUnit));
        return builder;
    }


    public static Builder addInterceptor(Builder builder, Interceptor interceptor) {
        builder.addInterceptor(interceptor);
        return builder;
    }

    public static Builder addNetworkInterceptor(Builder builder, Interceptor interceptor) {
        builder.addNetworkInterceptor(interceptor);
        return builder;
    }

    public static Builder cookieJar(Builder builder, NovateCookieManager cookie) {
        builder.cookieJar(cookie);
        return builder;
    }

    public static Builder addCache(Builder builder, Cache cache) {
        builder.cache(cache);
        return builder;
    }


    //---------------https的认证------------------------------

    /**
     * 通过所有https的认证。不做判断，不安全
     *
     * @param builder
     * @return
     */
    private static Builder addUnSafeSslSocketAndHostnameVerifier(Builder builder) {
        //如果设置了sslSocketFactory却没有配置对应的hostnameVerifier，那么Https请求是无法成功的
        //不设置会报：javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
        HttpsSslFactroy.SSLParams sslParams = HttpsSslFactroy.getSslSocketFactory(); //获取默认的SSLParams，通过所有认证
        addSSLSocketFactory(builder, sslParams.mSSLSocketFactory, sslParams.mTrustManager);

        //不设置请求https会报错：javax.net.ssl.SSLPeerUnverifiedException: Hostname xxx地址的host not verified:
        //设置ip授权认证：如果已经安装该证书，可以不设置，否则需要设置??????????????不设置会报错。。。待处理
        HostnameVerifier hostnameVerifier = HttpsSslFactroy.getHostnameVerifierUnSafe();
        return addHostnameVerifier(builder, hostnameVerifier);
    }

    /**
     * 单向认证
     *
     * @param builder
     * @param context
     * @param certificates 服务器需要验证的证书 把证书放到raw目录下
     * @return
     */
    public static Builder addSSLSocketFactory(Builder builder, Context context, @RawRes int[] certificates) {
        HttpsSslFactroy.SSLParams sslParams = HttpsSslFactroy.getSslSocketFactory(context, certificates);
        return addSSLSocketFactory(builder, sslParams.mSSLSocketFactory, sslParams.mTrustManager);
    }

    /**
     * 双向认证
     *
     * @param builder
     * @param context
     * @param certificates          服务器需要验证的证书 把证书放到raw目录下
     * @param clientKeyStoreBksFile 本地验证证书。一般双向验证才需要 把证书放到raw目录下
     * @param password              本地验证证书的密码
     * @return
     */
    public static Builder addSSLSocketFactory(Builder builder, Context context, @RawRes int[] certificates, @RawRes int clientKeyStoreBksFile, String password) {
        HttpsSslFactroy.SSLParams sslParams = HttpsSslFactroy.getSslSocketFactory(context, certificates, clientKeyStoreBksFile, password);
        return addSSLSocketFactory(builder, sslParams.mSSLSocketFactory, sslParams.mTrustManager);
    }

    /**
     * 双向认证
     *
     * @param builder
     * @param certificates 服务器需要验证的证书 把证书放到raw目录下
     * @param bksFile      本地验证证书。一般双向验证才需要 把证书放到raw目录下
     * @param password     本地验证证书的密码
     * @return
     */
    public static Builder addSSLSocketFactory(Builder builder, InputStream[] certificates, InputStream bksFile, String password) {
        HttpsSslFactroy.SSLParams sslParams = HttpsSslFactroy.getSslSocketFactory(certificates, bksFile, password);
        return addSSLSocketFactory(builder, sslParams.mSSLSocketFactory, sslParams.mTrustManager);
    }


    public static Builder addSSLSocketFactory(Builder builder, SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) {
        builder.sslSocketFactory(sslSocketFactory, trustManager);
        return builder;
    }


    //http://www.jianshu.com/p/16994e49e2f6
    //http://blog.csdn.net/sk719887916/article/details/51597816

    /**
     * 指定支持的host
     * hostnameVerifier对服务端返回的一些信息进行相关校验，用于客户端判断所连接的服务端是否可信，通常默认return true,或者简单校验hostname是否正确，默认不使用的话会调用okhttp的OkHostnameVerifier:
     * https://www.jianshu.com/p/1373889e74b2
     *
     * @param builder
     * @param hosts   指定支持的host
     * @return
     */
    public static Builder addHostnameVerifier(Builder builder, String[] hosts) {
        HostnameVerifier hostnameVerifier = HttpsSslFactroy.getHostnameVerifierSafe(hosts);
        return addHostnameVerifier(builder, hostnameVerifier);
    }


    public static Builder addHostnameVerifier(Builder builder, HostnameVerifier hostnameVerifier) {
        builder.hostnameVerifier(hostnameVerifier);
        return builder;
    }

    //---------------https的认证------------------------------

    /**
     * 添加证书Pinning
     *
     * @param builder
     * @param certificatePinner
     * @return
     */
    public static Builder addCertificatePinner(Builder builder, CertificatePinner certificatePinner) {
        builder.certificatePinner(certificatePinner);
        return builder;

        /**
         *
         * OkHttpClient client = new OkHttpClient.Builder()
         .certificatePinner(new CertificatePinner.Builder()
         .add("YOU API.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
         .add("YOU API..com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
         .add("YOU API..com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
         .add("YOU API..com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
         .build())
         *
         */
    }

    /**
     * 设置代理
     *
     * @param builder
     * @param proxy
     * @return
     */
    public static Builder proxy(Builder builder, Proxy proxy) {
        builder.proxy(proxy);
        return builder;
    }
}
